﻿using System;
using LoA.Shared.Screen;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace LoA.Screen {
	public partial class ScreenManager : DrawableGameComponent {
		public override void Draw(GameTime gameTime) {
			if (this.GameState != GameState.Playing) {
				switch (GameState) {
					case GameState.NotStarted:
						this.DrawScreen(this.currentScreen);
						this.DrawPopup(this.currentPopup);
						break;
					case GameState.Suspended:
						this.DrawPopup(this.currentPopup);
						break;
					case GameState.Loading:
						this.DrawStateScreen(GameState.Loading);
						break;
					case GameState.Resuming:
						this.DrawStateScreen(GameState.Resuming);
						break;
					case GameState.Suspending:
						this.DrawStateScreen(GameState.Suspending);
						break;
					case GameState.Closeing:
						this.DrawStateScreen(GameState.Closeing);
						break;
				}

				/// Fix GraphicDevice after spriteBatch for 3D Graphics
				GraphicsDevice.BlendState = BlendState.Opaque;
				GraphicsDevice.DepthStencilState = DepthStencilState.Default;
				GraphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;
			}

			base.Draw(gameTime);
		}

		private void DrawStateScreen(GameState gameState) {
			this.DrawBackgroundScreen( );
			int titleOffset = GraphicsDevice.Viewport.Height / 2;
			using (SpriteBatch spriteBatch = new SpriteBatch(GraphicsDevice)) {
				spriteBatch.Begin( );
				spriteBatch.DrawString(
					Engine.Fonts["Menu"],
					gameState.ToString( ),
					new Vector2(
						GraphicsDevice.Viewport.Width / 2 - Engine.Fonts["Menu"].MeasureString(gameState.ToString( )).X / 2,
						titleOffset - Engine.Fonts["Menu"].LineSpacing / 2
					),
					Color.White
				);
				spriteBatch.End( );
			}
		}

		private void DrawBackgroundScreen( ) {
			using (SpriteBatch spriteBatch = new SpriteBatch(GraphicsDevice)) {
				spriteBatch.Begin( );
				spriteBatch.Draw(
					menuBackgroundTexture,
					new Rectangle(
						0, 0,
						GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height
					),
					new Color(255, 255, 255)
				);
				spriteBatch.End( );
			}
		}

		private void DrawScreen(IScreen screen) {
			if (screen != null) {
				this.DrawBackgroundScreen( );
				int titleOffset = GraphicsDevice.Viewport.Height / 6;
				using (SpriteBatch spriteBatch = new SpriteBatch(GraphicsDevice)) {
					spriteBatch.Begin( );
					if (screen.Title != null)
						spriteBatch.DrawString(
							Engine.Fonts["Menu"],
							screen.Title,
							new Vector2(
								GraphicsDevice.Viewport.Width / 2 - Engine.Fonts["Menu"].MeasureString(screen.Title).X / 2,
								titleOffset + Engine.Fonts["Menu"].LineSpacing / 2
							),
							Color.White
						);
					spriteBatch.End( );
				}
				int order = 0;
				foreach (IScreenEntry entry in screen.Entries) {
					this.DrawScreenEntry(entry, order++);
				}
			}
		}

		private void DrawScreenEntry(IScreenEntry entry, int order) {
			if (entry is IChangeableScreenEntry) {
				this.DrawChangeableScreenEntry(entry as IChangeableScreenEntry, order);
				return;
			}
			int menuOffset = GraphicsDevice.Viewport.Height / 6 + Engine.Fonts["Menu"].LineSpacing * 2;
			using (SpriteBatch spriteBatch = new SpriteBatch(GraphicsDevice)) {
				spriteBatch.Begin( );
				if (entry.Name != null)
					if (this.selectedEntry == order)
						spriteBatch.DrawString(
							Engine.Fonts["Menu"],
							entry.Name,
							new Vector2(
								GraphicsDevice.Viewport.Width / 2 - Engine.Fonts["Menu"].MeasureString(entry.Name).X / 2,
								menuOffset + Engine.Fonts["Menu"].LineSpacing * order
							),
							Color.Yellow
						);
					else
						spriteBatch.DrawString(
							Engine.Fonts["Menu"],
							entry.Name,
							new Vector2(
								GraphicsDevice.Viewport.Width / 2 - Engine.Fonts["Menu"].MeasureString(entry.Name).X / 2,
								menuOffset + Engine.Fonts["Menu"].LineSpacing * order
							),
							Color.White
						);
				spriteBatch.End( );
			}
		}

		private void DrawChangeableScreenEntry(IChangeableScreenEntry changeableEntry, int order) {
			int menuOffset = GraphicsDevice.Viewport.Height / 6 + Engine.Fonts["Menu"].LineSpacing * 2;
			using (SpriteBatch spriteBatch = new SpriteBatch(GraphicsDevice)) {
				spriteBatch.Begin( );
				if (changeableEntry.Name != null) {
					string display = changeableEntry.Name + ": " + changeableEntry.CurrentValue;
					if (this.selectedEntry == order)
						spriteBatch.DrawString(
							Engine.Fonts["Menu"],
							display,
							new Vector2(
								GraphicsDevice.Viewport.Width / 2 - Engine.Fonts["Menu"].MeasureString(display).X / 2,
								menuOffset + Engine.Fonts["Menu"].LineSpacing * order
							),
							Color.Yellow
						);
					else
						spriteBatch.DrawString(
							Engine.Fonts["Menu"],
							display,
							new Vector2(
								GraphicsDevice.Viewport.Width / 2 - Engine.Fonts["Menu"].MeasureString(display).X / 2,
								menuOffset + Engine.Fonts["Menu"].LineSpacing * order
							),
							Color.White
						);
				}
				spriteBatch.End( );
			}
		}

		private void DrawPopup(IPopupScreenEntry popupEntry) {
			if (popupEntry != null) {
				int screenOffset = 5;
				int offsetMessage = Engine.Fonts["Menu"].LineSpacing / 2;

				int minWidth = GraphicsDevice.Viewport.Width * (screenOffset - 2) / screenOffset - GraphicsDevice.Viewport.Width / screenOffset;
				string wrapedMessage = popupEntry.Message.Wrap(minWidth, Engine.Fonts["Menu"]);

				int minHeight = offsetMessage;

				if (popupEntry.Title != null)
					minHeight += Engine.Fonts["Menu"].LineSpacing + offsetMessage;
				if (popupEntry.Message != null)
					minHeight += (int)Math.Ceiling(Engine.Fonts["Menu"].MeasureString(wrapedMessage).Y) + offsetMessage;
				if ((int)popupEntry.Buttons > 0)
					minHeight += Engine.Fonts["Menu"].LineSpacing + offsetMessage;
				if ((int)popupEntry.Buttons > 1)
					minHeight += Engine.Fonts["Menu"].LineSpacing + offsetMessage;
				if ((int)popupEntry.Buttons > 3)
					minHeight += Engine.Fonts["Menu"].LineSpacing + offsetMessage;

				using (SpriteBatch spriteBatch = new SpriteBatch(GraphicsDevice)) {
					spriteBatch.Begin( );
					spriteBatch.Draw(
						messageGradientTexture,
						new Rectangle(
							GraphicsDevice.Viewport.Width / screenOffset,
							GraphicsDevice.Viewport.Height / screenOffset,
							GraphicsDevice.Viewport.Width * (screenOffset - 2) / screenOffset,
							minHeight //GraphicsDevice.Viewport.Height * (screenOffset - 2) / screenOffset
						),
						new Color(200, 200, 200)
					);
					if (popupEntry.Title != null) {
						spriteBatch.DrawString(
							Engine.Fonts["Menu"],
							popupEntry.Title,
							new Vector2(
								GraphicsDevice.Viewport.Width / 2 - Engine.Fonts["Menu"].MeasureString(popupEntry.Title).X / 2,
								GraphicsDevice.Viewport.Height / screenOffset + offsetMessage
							),
							Color.White
						);
						offsetMessage += Engine.Fonts["Menu"].LineSpacing + offsetMessage;
					}
					if (popupEntry.Message != null) {
						spriteBatch.DrawString(
							Engine.Fonts["Menu"],
							wrapedMessage,
							new Vector2(
								GraphicsDevice.Viewport.Width / 2 - Engine.Fonts["Menu"].MeasureString(wrapedMessage).X / 2,
								GraphicsDevice.Viewport.Height / screenOffset + offsetMessage
							),
							Color.White
						);
						offsetMessage += (int)Math.Ceiling(Engine.Fonts["Menu"].MeasureString(wrapedMessage).Y) + offsetMessage;
					}
					switch (popupEntry.Buttons) {
						case EntryButtons.OK:
							const string messageOK = "A button, Space, Enter = ok";
							spriteBatch.DrawString(
								Engine.Fonts["Menu"],
								messageOK,
								new Vector2(
									GraphicsDevice.Viewport.Width / 2 - Engine.Fonts["Menu"].MeasureString(messageOK).X / 2,
									GraphicsDevice.Viewport.Height / screenOffset + offsetMessage
								),
								Color.White
							);
							break;
						case EntryButtons.OKCancel:
							string messageOKCancel = "A button, Space, Enter = ok\nB button, Esc = cancel";
							spriteBatch.DrawString(
								Engine.Fonts["Menu"],
								messageOKCancel,
								new Vector2(
									GraphicsDevice.Viewport.Width / 2 - Engine.Fonts["Menu"].MeasureString(messageOKCancel).X / 2,
									GraphicsDevice.Viewport.Height / screenOffset + offsetMessage
								),
								Color.White
							);
							break;
						case EntryButtons.YesNo:
							string messageYesNo = "A button, Space, Enter = yes\nB button, Esc = no";
							spriteBatch.DrawString(
								Engine.Fonts["Menu"],
								messageYesNo,
								new Vector2(
									GraphicsDevice.Viewport.Width / 2 - Engine.Fonts["Menu"].MeasureString(messageYesNo).X / 2,
									GraphicsDevice.Viewport.Height / screenOffset + offsetMessage
								),
								Color.White
							);
							break;
						case EntryButtons.YesNoCancel:
							string messageYesNoCancel = "A button, Space, Enter = yea\nB button, Esc = no\nC button, Esc = cancel?";
							spriteBatch.DrawString(
								Engine.Fonts["Menu"],
								messageYesNoCancel,
								new Vector2(
									GraphicsDevice.Viewport.Width / 2 - Engine.Fonts["Menu"].MeasureString(messageYesNoCancel).X / 2,
									GraphicsDevice.Viewport.Height / screenOffset + offsetMessage
								),
								Color.White
							);
							break;
					}
					spriteBatch.End( );
				}
			}
		}
	}

	public static class StringExtension {
		public static string Wrap(this string text, int maxWidth, SpriteFont font) {
			string[ ] textParts = text.Split(' ');
			string tempText = "";
			string labText = "";
			text = "";
			for (int i = 0; i < textParts.Length; i++) {
				if (i > 0)
					labText += " ";
				labText += textParts[i];

				if (font.MeasureString(labText).X > maxWidth) {
					text += tempText + "\n";
					labText = textParts[i];
				} else
					tempText = labText;
			}
			text += labText;
			return text;
		}
	}
}
